package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.service.AlbumAttributeValueService;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.config.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RabbitConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private AlbumAttributeValueMapper albumAttributeValueMapper;

	@Autowired
	private AlbumStatMapper albumStatMapper;

	@Autowired
	private AlbumAttributeValueService attributeValueService;

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private RedissonClient redissonClient;




	@Transactional(rollbackFor = Exception.class, timeout = 3)
	@Override
	public void saveAlbumInfo(AlbumInfoVo albumInfoVo) {
		//1.保存专辑表
		Long albumId = saveAlbum(albumInfoVo);
		//2.保存专辑标签值表
		this.attributeValueService.saveAlbumAttributeValue(albumInfoVo,albumId);

		//3.初始化专辑状态表
		initAlbumStat(albumId,SystemConstant.ALBUM_STAT_PLAY);
		initAlbumStat(albumId, SystemConstant.ALBUM_STAT_SUBSCRIBE);
		initAlbumStat(albumId, SystemConstant.ALBUM_STAT_BROWSE);
		initAlbumStat(albumId, SystemConstant.ALBUM_STAT_COMMENT);

		if (albumInfoVo.getIsOpen().equals("1")){
			this.rabbitTemplate.convertAndSend(RabbitConstant.ALBUM_EXCHANGE,RabbitConstant.ALBUM_SAVE_RK,albumId);
		}
	}

	private Long saveAlbum(AlbumInfoVo albumInfovo) {
		//1.保存专辑表
		AlbumInfo albumInfo = new AlbumInfo();
		//2.1 属性拷贝
		BeanUtils.copyProperties(albumInfovo, albumInfo);
		//设置主播id
		Long userId = AuthContextHolder.getUserId();//从THreadLocal中获取用户id
		albumInfo.setUserId(userId==null ? 1:userId);
		//设置免费试听声音数
		albumInfo.setTracksForFree(5);
		//审核状态
		albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
		this.save(albumInfo);
		Long albumId = albumInfo.getId();
		return albumId;
	}


	@Override
	public Page<AlbumListVo> findUserAlbumPage(Integer page, Integer limit, AlbumInfoQuery query) {
		// 设置当前主播的id
		Long userId = AuthContextHolder.getUserId();
		query.setUserId(userId == null ? 1 : userId);
		return this.albumInfoMapper.findUserAlbumPage(new Page<>(page, limit), query);
	}
	@GuiGuCache(prefix = RedisConstant.ALBUM_INFO_PREFIX,
			timeout = RedisConstant.ALBUM_TIMEOUT, random = RedisConstant.ALBUM_TEMPORARY_TIMEOUT,
			lock_prefix = RedisConstant.ALBUM_LOCK_PREFIX, bfName = RedisConstant.ALBUM_BLOOM_FILTER
	)
	@Override
	public AlbumInfo getAlbumInfo(Long albumId) {
		//查询专辑
		AlbumInfo albumInfo = this.getById(albumId);
		if (albumInfo==null){
			return albumInfo;
		}
		//专辑标签以及值
		List<AlbumAttributeValue> albumAttributeValues = this.albumAttributeValueMapper
				.selectList(new LambdaQueryWrapper<AlbumAttributeValue>()
						.eq(AlbumAttributeValue::getAlbumId, albumId));
		albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
		return albumInfo;
	}

	public AlbumInfo getAlbumInfo2(Long albumId) {

		// 1.先查询缓存，如果命中则直接返回
		String key = RedisConstant.ALBUM_INFO_PREFIX + albumId;
		AlbumInfo albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(key);
		if (albumInfo != null) {
			return albumInfo;
		}

		// 为了防止缓存穿透，使用了布隆过滤器
		RBloomFilter<String> bloomFilter = this.redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
		if (!bloomFilter.contains(key)) {
			return null;
		}
		// 为了防止缓存击穿，这里使用分布式锁
		RLock lock = this.redissonClient.getLock(RedisConstant.ALBUM_LOCK_PREFIX + albumId);
		lock.lock();

		try {
			// 当前请求获取锁的过程中，可能有其他请求提前获取了锁并把数据放入了缓存，再次查询缓存，如果命中则直接返回
			albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(key);
			if (albumInfo != null) {
				return albumInfo;
			}

			/// 2.查询数据库并放入缓存
			// 查询专辑
			albumInfo = this.getById(albumId);
			if (albumInfo == null) {
				return albumInfo;
			}
			// 专辑标签及值
			List<AlbumAttributeValue> albumAttributeValues = this.albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));
			albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);

			// 加入缓存：为了防止缓存雪崩，给缓存时间添加了随机值
			this.redisTemplate.opsForValue().set(RedisConstant.ALBUM_INFO_PREFIX + albumId, albumInfo, RedisConstant.ALBUM_TIMEOUT + new Random().nextLong(RedisConstant.CACHE_TEMPORARY_TIMEOUT), TimeUnit.SECONDS);

			return albumInfo;
		} finally {
			lock.unlock();
		}
	}

	@Override
	public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {
		// 更新专辑
		AlbumInfo albumInfo = new AlbumInfo();
		BeanUtils.copyProperties(albumInfoVo, albumInfo);
		// 设置专辑的id
		albumInfo.setId(albumId);
		this.updateById(albumInfo);

		// 先删除现有的专辑标签值
		this.albumAttributeValueMapper.delete(new LambdaUpdateWrapper<AlbumAttributeValue>()
				.eq(AlbumAttributeValue::getAlbumId, albumId));

		// 再进行新增专辑标签值
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
		albumAttributeValueVoList.forEach(albumAttributeValueVo -> {
			AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
			BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
			albumAttributeValue.setAlbumId(albumId);
			this.albumAttributeValueMapper.insert(albumAttributeValue);
		});
		if (albumInfo.getIsOpen().equals("1")){
			this.rabbitTemplate.convertAndSend(RabbitConstant.ALBUM_EXCHANGE,RabbitConstant.ALBUM_SAVE_RK,albumId);
		}else {
			this.rabbitTemplate.convertAndSend(RabbitConstant.ALBUM_EXCHANGE,RabbitConstant.ALBUM_DELETE_RK,albumId);
		}

	}

	@Override
	public void removeAlbumInfo(Long albumId) {
		//删除专辑
		this.removeById(albumId);
		//删除统计信息
		this.albumStatMapper.delete(new LambdaUpdateWrapper<AlbumStat>()
				.eq(AlbumStat::getAlbumId, albumId));
		//删除标签
		this.albumAttributeValueMapper.delete(new LambdaUpdateWrapper<AlbumAttributeValue>()
				.eq(AlbumAttributeValue::getAlbumId,albumId));

		this.rabbitTemplate.convertAndSend(RabbitConstant.ALBUM_EXCHANGE,RabbitConstant.ALBUM_DELETE_RK,albumId);
	}

	@Override
	public Page<AlbumListVo> findAlbumPage(Integer page, Integer limit) {
		return this.albumInfoMapper.findUserAlbumPage(new Page<>(page,limit),new AlbumInfoQuery());
	}

	@Override
	public List<AlbumAttributeValue> queryAttributeValues(Long albumId) {
		return this.albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>()
				.eq(AlbumAttributeValue::getAlbumId, albumId));
	}

	@Override
	public AlbumStatVo queryAlbumStats(Long albumId) {
		return this.albumStatMapper.queryAlbumStats(albumId);
	}

	private void initAlbumStat(Long albumId, String statType) {
		AlbumStat albumStat = new AlbumStat();
		albumStat.setAlbumId(albumId);
		albumStat.setStatType(statType);
		this.albumStatMapper.insert(albumStat);
	}
}
